/**
 *
 */
package sun.scheduler.core;

import java.awt.Point;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import sun.scheduler.core.job.Job;
import sun.scheduler.core.job.Job.CurveState;
import sun.scheduler.core.leveling.LevelingAlgorithm;
import sun.scheduler.core.resource.Resource;
import sun.scheduler.io.ScheduleReader;
import sun.scheduler.io.ScheduleWriter;

/**
 * Main class to start the program via the console by getting the file paths and
 * a duration (in min) for the project
 *
 * @author may
 *
 */
public class Main
{

    private static ArrayList<Point> initialCurve, roundetCurve, scheduledCurve;

    public static ArrayList<Point> getInitialCurve()
    {
        return initialCurve;
    }

    public static ArrayList<Point> getRoundetCurve()
    {
        return roundetCurve;
    }

    public static ArrayList<Point> getScheduledCurve()
    {
        return scheduledCurve;
    }

    /**
     * Computes the costcurve using the given jobs
     *
     * @param jobs
     */
    public static void computeCostCurve(ArrayList<Job> jobs)
    {
        initialCurve = new ArrayList<Point>();
        roundetCurve = new ArrayList<Point>();
        scheduledCurve = new ArrayList<Point>();

        ScheduleGraph sGraph = new ScheduleGraph(jobs);
        ScheduleOptimizer sOptimizer = new ScheduleOptimizer(sGraph);

        while (sOptimizer.hasNext())
        {
            // LTCT
            sOptimizer.next();
            Point p = new Point();
            p.setLocation(getTotalTime(sGraph), getTotalCost(sGraph));
            initialCurve.add(p);

            // ROUNDING
            sGraph.setCurveState(CurveState.Rounding);
            sGraph.round();

            p = new Point();
            p.setLocation(getTotalTime(sGraph), getTotalCost(sGraph));
            roundetCurve.add(p);

            // SCHEDULING
            sGraph.setCurveState(CurveState.Scheduling);
            sGraph.schedule();
            // don't update job time params

            p = new Point();
            p.setLocation(getTotalTime(sGraph), getTotalCost(sGraph));
            scheduledCurve.add(p);
        }
    }

    /**
     * Gets the total time of the {@link ScheduleGraph} at the current state.
     *
     * @param sGraph the graph which represents the a project schedule
     * @return the total time
     */
    public static long getTotalTime(ScheduleGraph sGraph)
    {
        // endtime is earliest startime of the target node:
        return sGraph.getTarget().getData().getEarliestStartTime();
    }

    /**
     * Gets the total cost of the {@link ScheduleGraph} at the current state.
     *
     * @param sGraph the graph which represents the a project schedule
     * @return the total cost
     */
    public static double getTotalCost(ScheduleGraph sGraph)
    {
        CurveState cState = sGraph.getCurveState();
        double totalCost = 0;
        for (Iterator<Job> iter = sGraph.jobIterator(); iter.hasNext();)
            totalCost += iter.next().getCost(cState);
        return totalCost;
    }

    /**
     * Computes the {@link ScheduleGraph} which corresponds to the specified
     * jobs and iteration.
     *
     * @param iteration the iteration
     * @param jobs the jobs to schedule
     * @return the corresponding {@link ScheduleGraph} instance
     */
    public static ScheduleGraph getScheduleGraph(int iteration, ArrayList<Job> jobs)
    {
        ScheduleGraph sGraph = new ScheduleGraph(jobs);
        ScheduleOptimizer sOptimizer = new ScheduleOptimizer(sGraph);
        
        System.out.println("cost " + Main.getTotalCost(sGraph));
        
        int i = 0;
        while (sOptimizer.hasNext() && i++ < iteration)
            sOptimizer.next();

        sGraph.setCurveState(CurveState.Rounding);
        sGraph.round();

        sGraph.setCurveState(CurveState.Scheduling);
        sGraph.schedule();

        return sGraph;
    }
    
    /**
     * Levels the given {@link ScheduleGraph}.
     *
     * @param sGraph the {@link ScheduleGraph} to level
     * @return sGraph (leveled)
     */
    public static ScheduleGraph level(ScheduleGraph sGraph)
    {
        sGraph.setCurveState(CurveState.Scheduling);
        LevelingAlgorithm lvlalg = new LevelingAlgorithm(sGraph, sGraph.getResourceProfiles());
        lvlalg.run();
        return sGraph;
    }

    /**
     * Levels the given {@link ScheduleGraph} with a priorized {@link Resource}.
     *
     * @param sGraph the {@link ScheduleGraph} to level
     * @param priorized the priorized {@link Resource}
     * @return sGraph (leveled)
     */
    public static ScheduleGraph level(ScheduleGraph sGraph, Resource priorized)
    {
        sGraph.setCurveState(CurveState.Scheduling);
        LevelingAlgorithm lvlalg = new LevelingAlgorithm(sGraph, sGraph.getResourceProfiles(), priorized);
        lvlalg.run();
        return sGraph;
    }

    /**
     * Main method for shell usage.
     *
     * @param args
     */
    public static void main(String args[])
    {

        try
        {

            if (!args[0].endsWith(".csv") || !args[1].endsWith(".csv")
                    || !args[2].endsWith(".csv") || !args[3].endsWith(".csv"))
            {
                System.err.println("Alle Dateien muessen mit '.csv' enden");
                return;
            }

            long time = Long.parseLong(args[4]);
            ArrayList<Resource> resources = ScheduleReader.readResourcesFromFile(args[1]);
            ArrayList<Job> jobInfos = ScheduleReader.readJobsFromFile(resources, args[0]);

            ScheduleGraph sGraph = new ScheduleGraph(jobInfos);
            ScheduleOptimizer sOptimizer = new ScheduleOptimizer(sGraph);

            while (sOptimizer.hasNext())
            {
                // LTCT
                sOptimizer.next();

                // ROUNDING
                sGraph.setCurveState(CurveState.Rounding);
                sGraph.round();
                sGraph.updateJobTimeParameters();

                // SCHEDULING
                sGraph.setCurveState(CurveState.Scheduling);
                sGraph.schedule();

                if (getTotalTime(sGraph) < time)
                    break;
            }

            ScheduleWriter.writeResourceFile(args[3], resources);
            ScheduleWriter.writeProcessFile(args[2], jobInfos, ScheduleReader.getStartDate(), Job.CurveState.Scheduling);
        
        }
        catch (NumberFormatException e)
        {
            System.err.println("Argument 5 ist keine Zahl");
            System.out.println("Eingabeformat: java <Programmname> <Jobdatei> <Ressourcendatei> "
                    + "<Ausgabedatei Jobs> <Ausgabedatei Ressourcen> <max. Projektdauer>");

        }
        catch (ArrayIndexOutOfBoundsException e)
        {
            System.err.println("Geben Sie Dateinamen und Projektdauer ein");
            System.out.println("Eingabeformat: java <Programmname> <Jobdatei> <Ressourcendatei> "
                    + "<Ausgabedatei Jobs> <Ausgabedatei Ressourcen> <max. Projektdauer>");

        }
        catch (NullPointerException e)
        {
            System.err.println("Geben Sie Dateinamen und Projektdauer ein");
            System.out.println("Eingabeformat: java <Programmname> <Jobdatei> <Ressourcendatei> "
                    + "<Ausgabedatei Jobs> <Ausgabedatei Ressourcen> <max. Projektdauer>");

        }
        catch (IndexOutOfBoundsException e)
        {
            System.err.println("In der gegebenen Zeit kann das Projekt nicht realisiert werden");

        }
        catch (Exception e)
        {
            System.err.println(e);
            e.printStackTrace();
        }
    }

}
